package com.seadragon.app.ksr.util.tiger;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.seadragon.app.ksr.model.DBFFileField;
import com.seadragon.app.ksr.model.DBFFileHeader;

public class DBFFileReader extends BufferedInputStream {
	private static final int VERSION_OFFSET = 0;
	private static final int LAST_UPDATE_DATE_YEAR_OFFSET = 1;
	private static final int LAST_UPDATE_DATE_MONTH_OFFSET = 2;
	private static final int LAST_UPDATE_DATE_DAY_OFFSET = 3;
	private static final int NUMBER_OF_RECORDS_OFFSET = 4;
	private static final int LENGTH_OF_HEADER_OFFSET = 8;
	private static final int LENGTH_OF_EACH_RECORD_OFFSET = 10;
	private static final int ENCRYPTION_FLAG_OFFSET = 15;
	private static final int FIELD_START_OFFSET = 32;
	private static final int LENGTH_OF_EACH_FIELD = 32;
	private static final int FIELD_NAME_OFFSET = 0;
	private static final int LENGTH_OF_NAME = 10;
	private static final int FIELD_TYPE_OFFSET = 11;
	private static final int FIELD_LENGTH_OFFSET = 16;

	private static final Logger logger = LoggerFactory.getLogger(DBFFileReader.class);
	private DBFFileHeader dbfFileHeader;
	private int count = 0;

	public DBFFileReader(File file) throws FileNotFoundException, IOException {
		super(new FileInputStream(file));
		byte[] bbuf = new byte[32];
		read(bbuf, 0, 32);
		dbfFileHeader = new DBFFileHeader();
		dbfFileHeader.setVersion(new Byte(bbuf[VERSION_OFFSET]).intValue());
		Calendar dateUpdated = Calendar.getInstance();
		dateUpdated.set(Calendar.HOUR_OF_DAY, 0);
		dateUpdated.set(Calendar.MINUTE, 0);
		dateUpdated.set(Calendar.SECOND, 0);
		dateUpdated.set(Calendar.MILLISECOND, 0);
		dateUpdated.set(Calendar.YEAR, 1900 + new Byte(bbuf[LAST_UPDATE_DATE_YEAR_OFFSET]).intValue());
		dateUpdated.set(Calendar.MONTH, new Byte(bbuf[LAST_UPDATE_DATE_MONTH_OFFSET]).intValue());
		dateUpdated.set(Calendar.DAY_OF_MONTH, new Byte(bbuf[LAST_UPDATE_DATE_DAY_OFFSET]).intValue());
		dbfFileHeader.setDateUpdated(dateUpdated.getTime());
		ByteBuffer ibuf = ByteBuffer.allocate(4);
		ibuf.put(3, bbuf[NUMBER_OF_RECORDS_OFFSET]);
		ibuf.put(2, bbuf[NUMBER_OF_RECORDS_OFFSET + 1]);
		ibuf.put(1, bbuf[NUMBER_OF_RECORDS_OFFSET + 2]);
		ibuf.put(0, bbuf[NUMBER_OF_RECORDS_OFFSET + 3]);

		dbfFileHeader.setNumberOfRecords(ibuf.asIntBuffer().get());
		ByteBuffer sbuf = ByteBuffer.allocate(2);
		sbuf.put(1, bbuf[LENGTH_OF_HEADER_OFFSET]);
		sbuf.put(0, bbuf[LENGTH_OF_HEADER_OFFSET + 1]);
		dbfFileHeader.setLengthOfHeader(sbuf.asShortBuffer().get());
		sbuf.put(1, bbuf[LENGTH_OF_EACH_RECORD_OFFSET]);
		sbuf.put(0, bbuf[LENGTH_OF_EACH_RECORD_OFFSET + 1]);
		dbfFileHeader.setLengthOfEachRecord(sbuf.asShortBuffer().get());
		dbfFileHeader.setEncrypted(new Byte(bbuf[ENCRYPTION_FLAG_OFFSET]).intValue() == 1 ? true : false);
		int offset = FIELD_START_OFFSET;
		while ((offset + LENGTH_OF_EACH_FIELD) < dbfFileHeader.getLengthOfHeader()) {
			DBFFileField dbfFileField = new DBFFileField();
			read(bbuf, 0, LENGTH_OF_EACH_FIELD);
			byte[] name = Arrays.copyOfRange(bbuf, FIELD_NAME_OFFSET, FIELD_NAME_OFFSET + LENGTH_OF_NAME);
			dbfFileField.setName(new String(name).trim());
			dbfFileField.setType((char) bbuf[FIELD_TYPE_OFFSET]);
			dbfFileField.setLength(bbuf[FIELD_LENGTH_OFFSET]);
			dbfFileHeader.add(dbfFileField);
			offset += LENGTH_OF_EACH_FIELD;
		}
		pos = dbfFileHeader.getLengthOfHeader();
	}

	public List<Object> next() throws IOException {
		byte[] bbuf = new byte[dbfFileHeader.getLengthOfEachRecord()];
		List<Object> record = new ArrayList<Object>();
		read(bbuf, 0, dbfFileHeader.getLengthOfEachRecord());
		int offset = 1;
		for (DBFFileField field : dbfFileHeader.getFields()) {
			byte[] value = Arrays.copyOfRange(bbuf, offset, offset + field.getLength());
			offset += field.getLength();
			if ('C' == field.getType()) {
				record.add(new String(value).trim());
			} else if ('N' == field.getType()) {
				try{
					record.add(Long.valueOf(new String(value).trim()));
				}catch(NumberFormatException ex){
					record.add(0l);
				}
			}
		}
		count++;
		return record;
	}

	public boolean hasNext() {
		if (count < dbfFileHeader.getNumberOfRecords()) {
			return true;
		}
		return false;
	}

	public static void main(String[] argv) {
		DBFFileReader dbfFileReader = null;
		try {
			dbfFileReader = new DBFFileReader(new File("data\\tl_2012_24031_edges\\tl_2012_24031_edges.dbf"));
			while (dbfFileReader.hasNext()) {
//				List<Object> record = dbfFileReader.next();
//				logger.info(record.toString());
			}
			logger.info("Total number of records are processed: " + dbfFileReader.count );
		} catch (Exception ex) {
			logger.info("Total number of records are processed: " + dbfFileReader.count );
			logger.error(ex.getMessage(), ex);
		} finally {
			if (dbfFileReader != null) {
				try {
					dbfFileReader.close();
				} catch (IOException ex) {

				}
			}
		}
	}
}
